#ifndef MONTGOMERY_CMODEL_H_
#define MONTGOMERY_CMODEL_H_
#include <cstdint>
#include <cstddef>

#include <openssl/bn.h>
#include <openssl/evp.h>


/*
 * @brief: 目标是计算和检验校验和
 * @param[in] data: 数据, little-endian
 * @param[in] byte_len: 数据字节长度
 * @return 16位校验和
 */
uint16_t checksum(unsigned char *data, size_t byte_len);

/*
 * @brief: 模加，目标是输出 a1+b1 mod p, a2+b2 mod p, ...
 * @param[in] count: 操作数的数目
 * @param[in][out] a: 操作数，包含count个值，每个值有p_bitcount个比特，同时也是输出存放的位置
 * @param[in] b: 另一个操作数，包含count个值，每个值有p_bitcount个比特
 * @param[in] p: 模数
 * @param[in] p_bitcount: 模数的比特数，确保是64的倍数，一般而言是2048或4096
 */
void mod_add(int count, char *a, char *b, char *p, int p_bitcount);

/*
 * @brief: 蒙哥马利模乘，目标是输出 a1*b1 mod p, a2*b2 mod p, ...
 * @param[in] count: 操作数的数目
 * @param[in][out] a: 操作数，包含count个值，每个值有p_bitcount个比特，同时也是输出存放的位置
 * @param[in] b: 另一个操作数，包含count个值，每个值有p_bitcount个比特
 * @param[in] p: 模数
 * @param[in] p_bitcount: 模数的比特数，确保是64的倍数，一般而言是2048或4096
 * @param[in] n_prime: 蒙哥马利需要的n'的值，有p_bitcount个比特
 * @param[in] r_square: 蒙哥马利需要的r^2的值，有p_bitcount个比特
 */
void mod_mul(int count, char *a, char *b, char *p, int p_bitcount, char *n_prime, char *r_square);

void mod_mul_ssl(int count, char *a, char *b, char *p, int p_bitcount, char *n_prime, char *r_square);


/*
 * @brief: 蒙哥马利模乘，其中一个操作数是常数，目标是输出 a1*b mod p, a2*b mod p, ...
 * @param[in] count: 操作数的数目
 * @param[in][out] a: 操作数，包含count个值，每个有p_bitcount个比特，同时也是输出存放的位置
 * @param[in] b: 另一个操作数，包含1个值，有p_bitcount个比特
 * @param[in] p: 模数
 * @param[in] p_bitcount: 模数的比特数，确保是64的倍数，一般而言是2048或4096
 * @param[in] n_prime: 蒙哥马利需要的n'的值，有p_bitcount个比特
 * @param[in] r_square: 蒙哥马利需要的r^2的值，有p_bitcount个比特
 */
void mod_mul_const(int count, char *a, char *b, char *p, int p_bitcount, char *n_prime, char *r_square);

/*
 * @brief: 蒙哥马利模幂，目标是输出 a1^e1 mod p, a2^e2 mod p, ...
 * @param[in] count: 操作数的数目
 * @param[in] a: 底数，包含count个值，每个有p_bitcount个比特
 * @param[in] e: 指数，包含count个值，每个值有e_bitcount个比特
 * @param[in] e_bitcount: 模数的比特数，确保是8的倍数
 * @param[in] p: 模数
 * @param[in] p_bitcount: 模数的比特数，确保是64的倍数，一般而言是2048或4096
 * @param[in] n_prime: 蒙哥马利需要的n'的值，有p_bitcount个比特
 * @param[in] r_square: 蒙哥马利需要的r^2的值，有p_bitcount个比特
 * @param[out] output: 输出存放的位置，包含count个值，每个有p_bitcount个比特
 */
void mod_exp(int count, char *a, char *e, int e_bitcount, char *p, int p_bitcount, char *n_prime, char *r_square, char *output);

void mod_exp_ssl(int count, char *a, char *e, int e_bitcount, char *p, int p_bitcount, char *n_prime, char *r_square, char *output);

/*
 * @brief: 蒙哥马利模幂，底数是常数，目标是输出 a^e1 mod p, a^e2 mod p, ...
 * @param[in] count: 操作数的数目
 * @param[in] a: 底数，包含count个值，每个有p_bitcount个比特
 * @param[in] e: 指数，包含count个值，每个值有e_bitcount个比特
 * @param[in] e_bitcount: 模数的比特数，确保是8的倍数
 * @param[in] p: 模数
 * @param[in] p_bitcount: 模数的比特数，确保是64的倍数，一般而言是2048或4096
 * @param[in] n_prime: 蒙哥马利需要的n'的值，有p_bitcount个比特
 * @param[in] r_square: 蒙哥马利需要的r^2的值，有p_bitcount个比特
 * @param[out] output: 输出存放的位置，包含count个值，每个有p_bitcount个比特
 */
void mod_const_exp(int count, char *a, char *e, int e_bitcount, char *p, int p_bitcount, char *n_prime, char *r_square, char *output);

/*
 * @brief: 蒙哥马利模乘模幂实现的加密內积，目标是输出 a1^b1 + a2^b2 + ... mod p
 * @param[in] count: 操作数的数目
 * @param[in] a: 乘数，包含count个值，每个有p_bitcount个比特
 * @param[in] b: 乘数，包含count个值，每个值有p_bitcount个比特
 * @param[in] p: 模数
 * @param[in] p_bitcount: 模数的比特数，确保是64的倍数，一般而言是2048或4096
 * @param[in] n_prime: 蒙哥马利需要的n'的值，有p_bitcount个比特
 * @param[in] r_square: 蒙哥马利需要的r^2的值，有p_bitcount个比特
 * @param[out] output: 输出存放的位置，包含1个值，有p_bitcount个比特
 */
void mod_dot(int count, char *a, char *b, char *p, int p_bitcount, char *n_prime, char *r_square, char *output);

/*
 * @brief: 蒙哥马利模乘模幂实现的加密內积，目标是输出 a1^e1 * a2^e2 * ... mod p
 * @param[in] count: 操作数的数目
 * @param[in] a: 底数，包含count个值，每个有p_bitcount个比特
 * @param[in] e: 指数，包含count个值，每个值有e_bitcount个比特
 * @param[in] e_bitcount: 模数的比特数，确保是8的倍数
 * @param[in] p: 模数
 * @param[in] p_bitcount: 模数的比特数，确保是64的倍数，一般而言是2048或4096
 * @param[in] n_prime: 蒙哥马利需要的n'的值，有p_bitcount个比特
 * @param[in] r_square: 蒙哥马利需要的r^2的值，有p_bitcount个比特
 * @param[out] output: 输出存放的位置，包含1个值，有p_bitcount个比特
 */
void mod_exp_dot(int count, char *a, char *e, int e_bitcount, char *p, int p_bitcount, char *n_prime, char *r_square, char *output);

/*
 * @brief: 实现模数上求逆，目标是输出 a1^{-1} mod p, a2^{-1} mod p, ...
 * @param[in] count: 操作数的数目
 * @param[in][out] a: 包含count个值，每个有p_bitcount个比特，也是输出存放的位置
 * @param[in] p: 模数
 * @param[in] p_bitcount: 模数的比特数，确保是64的倍数，一般而言是2048或4096
 */
void mod_inverse(int count, char *a, char *p, int p_bitcount);

/*
 * @brief: 生成count个的随机数 r1, r2, ...
 * @param[in] count: 操作数的数目
 * @param[in] seed: 32位种子
 * @param[in] bitcount: 每个随机数的比特位数
 * @param[in] output: 输出地址，有count个数，每个数是bitcount位
 */
void generate_pseudorandom(int count, unsigned char *seed, int bitcount, char *output);

/*
 * @brief: 多线程生成count个的随机数 r1, r2, ...
 * @param[in] count: 操作数的数目
 * @param[in] seed: 32位种子
 * @param[in] bitcount: 每个随机数的比特位数
 * @param[in] thread_num: 可用线程数
 * @param[in] output: 输出地址，有count个数，每个数是bitcount位
 */
void generate_pseudorandom_multhread(int count, unsigned char *seed, int bitcount, char *output, int thread_num);

/*
 * @brief: 期待的软件进入硬件的入口
 * @param[in] opcode: 操作码，目前而言4个bit足够，映射关系是：
 *                    0 -> 向量模加 mod_add
 *                    1 -> 向量模乘 mod_mul
 *                    2 -> 向量常数模乘 mod_mul_const
 *                    3 -> 向量模幂 mod_exp
 *                    4 -> 常数向量模幂 mod_const_exp
 *                    5 -> 同态乘法指数 mod_dot
 *                    6 -> 向量指数点积 mod_exp_dot
 *                    7 -> 批量求逆 mod_inverse
 *                    8 -> 批量生成随机数 generate_random
 * @param[in] count: 此次操作的操作数的个数
 * @param[in] p_bitcount: 模数的比特数，确保是64的倍数，一般而言是2048或4096
 * @param[in] input: 输入的内容，硬件需要将这些内容都读进去
 * @param[out] output: 输出的内容，硬件有可能需要开辟一块单独的区域存放，也可能复用已有的输入空间，能否复用取决于具体操作
 */
void operation(char opcode, int count, int p_bitcount, char *input, char **output);

#endif  // MONTGOMERY_CMODEL_H_
